/*
 *  $Id: ParticleTrajectory.cs 1191 2010-11-12 16:17:29Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008-2009 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections.Generic;

using AgateLib.Geometry;

using Ragima.Helper;

namespace Ragima.UI {
    /// <summary>
    /// Стандартные генераторы траекторий частиц.
    /// </summary>
    public static class ParticleTrajectory {
        /// <summary>
        /// Траектория висения в одной заданной точек.
        /// </summary>
        public static IEnumerable<PointF> Point(PointF point) {
            while (true)
                yield return point;
        }

        /// <summary>
        /// Траектория по линии с заданными углом и скоростью.
        /// </summary>
        public static IEnumerable<PointF> Line(double angle, double speed) {
            double a = angle.ToRadians();
            double xa = Math.Cos(a), ya = Math.Sin(a);
            double r = 0;
            while (true) {
                float x = (float) (r * xa);
                float y = (float) (r * ya);
                yield return new PointF(x, y);
                r += speed;
            }
        }
        /// <summary>
        /// Траектория по линии между двумя прямоугольниками.
        /// </summary>
        public static IEnumerable<PointF> FromTo(Rectangle cardA, Rectangle cardB, double speed) {
            int sideA = (cardA.X + cardA.Width / 2) - (cardB.X + cardB.Width / 2);
            int sideB = (cardA.Y + cardA.Height / 2) - (cardB.Y + cardB.Height / 2);
            double angle = new Point(sideA,sideB).GetAngle();
            return Line(angle, speed);
        }

        /// <summary>
        /// Траектория по кругу с заданными начальным углом, радиусом, и скоростью вращения.
        /// </summary>
        public static IEnumerable<PointF> Circle(double angle, double radius, double speedAngle) {
            return Spiral(angle, radius, speedAngle, 0);
        }

        /// <summary>
        /// Траектория по спирали от нуля с заданными начальным углом, скоростью вращения, и скоростью расхождения.
        /// </summary>
        public static IEnumerable<PointF> SpiralFrom0(double angle, double speedAngle, double speedRadius) {
            return Spiral(angle, 0, speedAngle, speedRadius);
        }

        /// <summary>
        /// Траектория по спирали с заданными начальным углом, начальным радиусом, скоростью вращения, и скоростью расхождения.
        /// </summary>
        public static IEnumerable<PointF> Spiral(double angle, double radius, double speedAngle, double speedRadius) {
            var polar = new PolarPoint(angle, radius);
            while (true) {
                yield return polar.ToPointF();
                polar.ChangeBy(speedAngle, speedRadius);
            }
        }

        #region Мета-траектории
        /// <summary>
        /// Траектория, сдвигающая все точки другой траектории на заданное значение.
        /// </summary>
        public static IEnumerable<PointF> Shift(IEnumerable<PointF> trajectory, PointF shift) {
            foreach (PointF point in trajectory)
                yield return point.Add(shift);
        }

        /// <summary>
        /// Траектория, складывающая вместе несколько других траекторий.
        /// </summary>
        public static IEnumerable<PointF> Group(params IEnumerable<PointF>[] trajectories) {
            int count = trajectories.Length;
            if (count == 0)
                yield break;
            IEnumerator<PointF>[] enums = new IEnumerator<PointF>[count];
            for (int i = 0; i < count; i++)
                enums[i] = trajectories[i].GetEnumerator();
            while (count > 0) {
                PointF point = PointF.Empty;
                bool ended = false;
                for (int i = 0; i < count; i++) {
                    IEnumerator<PointF> t = enums[i];
                    if (t.MoveNext()) {
                        point.X += t.Current.X;
                        point.Y += t.Current.Y;
                    } else {
                        t.Dispose();
                        enums[i] = null;
                        ended = true;
                    }
                }
                yield return point;
                if (ended) {
                    int p = 0;
                    for (int i = 0; i < count; i++) {
                        if (enums[i] != null) {
                            if (i != p)
                                enums[p] = enums[i];
                            p++;
                        }
                    }
                    count = p;
                }
            }
        }
        #endregion
    }
}
